/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2021 Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 * Copyright (c) 2021 Abrar (https://gitlab.com/s96Abrar)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 **/

#include <QtCore>

#include <qpa/qplatformnativeinterface.h>
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"

#include "wayqt/XdgTopLevel.hpp"

WQt::XdgTopLevel::XdgTopLevel( xdg_surface *surf, xdg_toplevel *toplevel ) {
    mSurfObj = surf;
    mObj     = toplevel;

    xdg_surface_add_listener( mSurfObj, &mSurfListener, this );
    xdg_toplevel_add_listener( mObj, &mListener, this );
}


WQt::XdgTopLevel::~XdgTopLevel() {
    xdg_toplevel_destroy( mObj );
    xdg_surface_destroy( mSurfObj );
}


void WQt::XdgTopLevel::setParent( XdgTopLevel *parent ) {
    xdg_toplevel_set_parent( mObj, parent->get() );
}


void WQt::XdgTopLevel::setTitle( QString title ) {
    xdg_toplevel_set_title( mObj, title.toUtf8().data() );
}


void WQt::XdgTopLevel::setAppId( QString appid ) {
    xdg_toplevel_set_app_id( mObj, appid.toUtf8().data() );
}


void WQt::XdgTopLevel::showWindowMenu( wl_seat *seat, quint32 serial, const QPoint& pos ) {
    xdg_toplevel_show_window_menu( mObj, seat, serial, pos.x(), pos.y() );
}


void WQt::XdgTopLevel::move( wl_seat *seat, quint32 serial ) {
    xdg_toplevel_move( mObj, seat, serial );
}


void WQt::XdgTopLevel::resize( wl_seat *seat, quint32 serial, Qt::Edges edges ) {
    xdg_toplevel_resize( mObj, seat, serial, (uint32_t)edges );
}


void WQt::XdgTopLevel::setMaximumSize( const QSize& size ) {
    xdg_toplevel_set_max_size( mObj, size.width(), size.height() );
}


void WQt::XdgTopLevel::setMinimumSize( const QSize& size ) {
    xdg_toplevel_set_min_size( mObj, size.width(), size.height() );
}


void WQt::XdgTopLevel::setMaximized() {
    xdg_toplevel_set_maximized( mObj );
}


void WQt::XdgTopLevel::unsetMaximized() {
    xdg_toplevel_unset_maximized( mObj );
}


void WQt::XdgTopLevel::setFullscreen( wl_output *output ) {
    xdg_toplevel_set_fullscreen( mObj, output );
}


void WQt::XdgTopLevel::unsetFullscreen() {
    xdg_toplevel_unset_fullscreen( mObj );
}


void WQt::XdgTopLevel::setMinimized() {
    xdg_toplevel_set_minimized( mObj );
}


QSize WQt::XdgTopLevel::size() {
    return tlSize;
}


void WQt::XdgTopLevel::setSize( QSize size ) {
    tlSize = size;
    resized( tlSize );
}


xdg_toplevel *WQt::XdgTopLevel::get() {
    return mObj;
}


xdg_surface *WQt::XdgTopLevel::xdgSurface() {
    return mSurfObj;
}


void WQt::XdgTopLevel::handleSurfaceConfigure( void *data, struct xdg_surface *surf, uint32_t serial ) {
    Q_UNUSED( surf );
    XdgTopLevel *tl = reinterpret_cast<XdgTopLevel *>(data);
    emit tl->configureRequested( tl->pendingSize, tl->pendingStates, serial );

    if ( not tl->pendingSize.isNull() ) {
        tl->setSize( tl->pendingSize );
        tl->pendingSize = QSize();
    }

    tl->pendingStates = {};
}


void WQt::XdgTopLevel::handleTopLevelConfigure( void *data, struct xdg_toplevel *, int32_t w, int32_t h, struct wl_array *state ) {
    XdgTopLevel *tl = reinterpret_cast<XdgTopLevel *>(data);
    States      states;

    uint32_t *statePtr = static_cast<uint32_t *>(state->data);

    for ( size_t i = 0; i < state->size / sizeof(uint32_t); i++ ) {
        switch ( statePtr[ i ] ) {
            case XDG_TOPLEVEL_STATE_MAXIMIZED: {
                states = states | XdgTopLevel::State::Maximized;
                break;
            }

            case XDG_TOPLEVEL_STATE_FULLSCREEN: {
                states = states | XdgTopLevel::State::Fullscreen;
                break;
            }

            case XDG_TOPLEVEL_STATE_RESIZING: {
                states = states | XdgTopLevel::State::Resizing;
                break;
            }

            case XDG_TOPLEVEL_STATE_ACTIVATED: {
                states = states | XdgTopLevel::State::Activated;
                break;
            }

            case XDG_TOPLEVEL_STATE_TILED_LEFT: {
                states = states | XdgTopLevel::State::TiledLeft;
                break;
            }

            case XDG_TOPLEVEL_STATE_TILED_RIGHT: {
                states = states | XdgTopLevel::State::TiledRight;
                break;
            }

            case XDG_TOPLEVEL_STATE_TILED_TOP: {
                states = states | XdgTopLevel::State::TiledTop;
                break;
            }

            case XDG_TOPLEVEL_STATE_TILED_BOTTOM: {
                states = states | XdgTopLevel::State::TiledBottom;
                break;
            }
        }
    }

    tl->pendingSize   = QSize( w, h );
    tl->pendingStates = states;
}


void WQt::XdgTopLevel::handleTopLevelClose( void *data, struct xdg_toplevel * ) {
    XdgTopLevel *tl = reinterpret_cast<XdgTopLevel *>(data);
    emit tl->closeRequested();
}


const xdg_surface_listener WQt::XdgTopLevel::mSurfListener = {
    handleSurfaceConfigure
};

const xdg_toplevel_listener WQt::XdgTopLevel::mListener = {
    handleTopLevelConfigure,
    handleTopLevelClose
};
